import java.io.Serializable;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
 * Classe controleur et application (système)
 * @author IUT,   A. Culet
 * @version 1.0 
 */


public class Controleur implements Serializable{
	
	private static final long serialVersionUID = 1L;

	/**
	 * La classe Controleur est unique pour tous les cas d'utilisation
	 * Elle est également la classe "application" qui gère l'ensemble des objets de l'appli
	 */	
	// ************************************************************************************************************
		// Attributs
		// ************************************************************************************************************
	
		// Attributs d'Association
		// Ensemble des ouvrages de la bibliothèque
		private HashMap<String, Ouvrage> _ouvrages;
		private HashMap<String, Auteur> _auteurs;
		private HashMap<Integer, Lecteur> _lecteurs;
		private HashMap<String, Periodique> _periodiques;
		private Quotas _quotas;
		private int derNumLect = 0;
		
		// la liste des vues. La 1ere est toujours la vue Menu Principal. La dernière est la vue active.
		private LinkedList<Vue> _vues;
		
		
		// ************************************************************************************************************
		// Constructeur
		// ************************************************************************************************************

		public Controleur() {
			this.setOuvrages(new HashMap<String, Ouvrage>());
			this.setAuteurs(new HashMap<String, Auteur>());
			this.setPeriodiques(new HashMap<String, Periodique>());
			this.setLecteurs(new HashMap<Integer, Lecteur>());
			_vues = new LinkedList<Vue>();
		} // Fin Controleur

		// ************************************************************************************************************
		// Méthodes privées
		// ************************************************************************************************************

		// ------------------------------------------------------------------------------------------------------------
		// Affecteurs
		
		/**
		 * Ajoute un ouvrage à l'ensemble des ouvrages de la bibliothèque.
		 * @param ouvrage 	Ouvrage à ajouter
		 * @param isbn 	code ISBN de cet ouvrage
		 */
		private void setOuvrage(Ouvrage ouvrage, String isbn) {
			this.getOuvrages().put(isbn, ouvrage);
		} // Fin setOuvrage
		
		private void setPeriodique(Periodique periodique, String issn) {
			this.getPeriodiques().put(issn, periodique);
		} //Fin setPeriodique
		
		private void setAuteur(Auteur auteur, String nom) {
			this.getAuteurs().put(nom,auteur);
		} // Fin setAuteur
		
		private void setLecteur(Lecteur lecteur, int num) {
			this.getLecteurs().put(num, lecteur);
		} // Fin setLecteur
		
		/**
		 * @param ouvrages hashtable d'ouvrages à affecter
		 */
		private void setOuvrages(HashMap<String, Ouvrage> ouvrages) {
			_ouvrages = ouvrages;
		}// Fin setOuvrages
		
		private void setAuteurs(HashMap<String, Auteur> auteurs) {
			_auteurs = auteurs;
		}// Fin setAuteurs
		
		private void setPeriodiques(HashMap<String, Periodique> periodiques) {
			_periodiques = periodiques;
		}
		
		private void setQuotas(Quotas quotas) {
			_quotas=quotas;
		}
		
		private void setLecteurs(HashMap<Integer, Lecteur> lecteurs) {
			_lecteurs=lecteurs;
		}
		
		/**
		 * ajoute ou enlève la vue active courante de la liste des vues
		 * @param vue  la vue à affecter
		 */
		 private void setVue(Vue vue) {
				_vues.addLast(vue);
		 }
		 private void removeVue() {
				_vues.removeLast();
		 }
		
		/*
		// ------------------------------------------------------------------------------------------------------------
		// Accesseurs
		
		/**
		 * @return ensemble des ouvrages de la bibliothèque
		 */
		private HashMap<String, Ouvrage> getOuvrages() {
			return _ouvrages;
		}// Fin getOuvrages
		
		public HashMap<String, Auteur> getAuteurs() {
			return _auteurs;
		}// Fin getOuvrages
		
		private HashMap<String, Periodique> getPeriodiques() {
			return _periodiques;
		}// Fin getOuvrages
		
		private HashMap<Integer, Lecteur> getLecteurs() {
			return _lecteurs;
		}// Fin getLecteurs
		
		private Quotas getQuotas() {
			return _quotas;
		}// Fin getQuotas

		/**
		 * Accès à un ouvrage par son numéro ISBN
		 * @param isbn 	le code ISBN de l'ouvrage cherché
		 * @return l'ouvrage quiString a l'ISBN indiqué
		 */
		private Ouvrage getOuvrage(String isbn) {
			return this.getOuvrages().get(isbn);
		} // Fin getOuvrage
		
		private Lecteur getLecteur(int numLect) {
			return this.getLecteurs().get(numLect);
		} // Fin getLecteur
		
		/**
		 * Accès à un périodique par son numéro ISSN
		 * @param issn 	le code ISSN du périodique cherché
		 * @return le périodique qui a l'ISSN indiqué
		 */
		private Periodique getPeriodique(String issn) {
			return this.getPeriodiques().get(issn);
		}
		
		private Auteur getAuteur(String auteur){
			if (this.getAuteurs()!=null){
				return this.getAuteurs().get(auteur);
			}
			else return null;			
		}
		

		/**
		 * @return la vue active courante 
		 */
		private Vue getVue() {
			return (Vue)_vues.getLast() ;
		}
		/**
		 * @return la vue Menu Principal 
		 */
		private VueMenuBiblio getVueMenu() {
			return (VueMenuBiblio)_vues.getFirst() ;
		}
		
		/*
		// ************************************************************************************************************
		// Méthodes publiques de création et affichage des fenêtres de l'application et fermeture
		// ************************************************************************************************************
		/**
		 * Création et affichage de la fenêtre principale de l'application. 
		 * Elle propose le menu de l'appli
		 */
		
		public void menuBiblio() {
			try {this.setVue(new VueMenuBiblio(this));
				this.getVueMenu().setVisible(true);
				
				if (_quotas==null){
					_quotas= new Quotas(5,14,(float)0.50);
				}
					
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		/**
		 * Cas d'utilisation : consultation d'un ouvrage
		 * Création et affichage de la fenêtre de consultation d'un ouvrage
		 */
		public void rechercheParAuteur(){
			try {this.setVue (new VueRechercherParAuteur(this));
			// le Menu est caché
				this.getVueMenu().setVisible(false); 	
			// la vue courante est VueConsultOuvrage
				this.getVue().setEtat(Vue.initiale);
				
				((VueRechercherParAuteur)this.getVue()).alimente(this.getAuteurs());
				
				this.getVue().setVisible(true);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		public void consulterOuvrage() {
			try {this.setVue (new VueConsultOuvrage(this));
			// le Menu est caché
				this.getVueMenu().setVisible(false); 	
			// la vue courante est VueConsultOuvrage
				this.getVue().setEtat(Vue.initiale);
				this.getVue().setVisible(true);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		public void consulterPeriodique() {
			try {this.setVue (new VueConsultPeriodique(this));
			// le Menu est caché
			this.getVueMenu().setVisible(false); 	
		// la vue courante est VueConsultPeriodique
			this.getVue().setEtat(Vue.initiale);
			this.getVue().setVisible(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
		}
		
		public void saisirPeriodique() {
			try {this.setVue (new VueSaisiePeriodique(this));
			// le Menu est caché
				 this.getVueMenu().setVisible(false);
			// la vue courante est VueSaisiePeriodique
				 this.getVue().setEtat(Vue.initiale);
				 this.getVue().setVisible(true);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		/**
		 * Cas d'utilisation : saisie d'un exemplaire d'ouvrage
		 * Création et affichage de la fenêtre de saisie d'un exemplaire d'ouvrage
		 */
		public void saisirExemplaire() {
			try {this.setVue(new VueSaisieExemplaire(this));
			// le Menu est caché
				this.getVueMenu().setVisible(false); 
			// la vue courante est VueSaisieExemplaire
				this.getVue().setEtat(Vue.initiale);
				this.getVue().setVisible(true);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		
		/**
		 * cas d'utilisation : saisie d'un ouvrage
		 * Création et affichage de la fenêtre de saisie d'un ouvrage
		 */
		public void saisirOuvrage() {
			try {this.setVue(new VueSaisieOuvrage(this,this.getAuteurs()));
			// le Menu est caché
				this.getVueMenu().setVisible(false); 
			// la vue courante est VueSaisieOuvrage
				this.getVue().setEtat(Vue.initiale);
				((VueSaisieOuvrage)this.getVue()).alimente(this.getAuteurs());
				this.getVue().setVisible(true);
			} catch (Exception e) {
			e.printStackTrace();
			}
		}
		
		public void saisirLecteur() {
			try {this.setVue(new VueSaisieLecteur(this));
				 this.getVueMenu().setVisible(false);
				 this.getVue().setEtat(Vue.initiale);
				 this.getVue().setVisible(true);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		public void saisirQuotas() {
			try {this.setVue(new VueParametrageQuotas(this));
			// le Menu est caché
				this.getVueMenu().setVisible(false);
			// la vue courante est VueParametrageQuotas
				this.getVue().setEtat(Vue.initiale);
				((VueParametrageQuotas)this.getVue()).alimente(this.getQuotas());
				this.getVue().setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
		}
		
		public void consultLecteur() {
			try {this.setVue(new VueConsultLecteur(this));
			// le Menu est caché
				this.getVueMenu().setVisible(false);
			// la vue courante est VueParametrageQuotas
				this.getVue().setEtat(Vue.initiale);
				this.getVue().setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
		}		
		
		public void saisirParution() {
			try {this.setVue(new VueSaisieParution(this));
			// le Menu est caché
				this.getVueMenu().setVisible(false);
			// la vue courante est VueParametrageQuotas
				this.getVue().setEtat(Vue.initiale);
				this.getVue().setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
		}
		
		public void rechercheInfosAuteur(String auteur){
			Auteur au;
			if (auteur==null){
				Message dialogue= new Message("Vous n'avez pas choisit d'auteur");
			}
			else
			{
				au=this.getAuteur(auteur);
				if (au==null){
					Message dialogue= new Message("L'auteur n'existe pas");
				}
				else
				{
					((VueRechercherParAuteur)this.getVue()).alimenteInfos(au.getLesOuvrages(),au.getArticles());
				}
				
				
				
			}
		}
		/**
		 * fermeture de la fenêtre vue
		 * lors de la fermeture de la fenêtre principale de l'application sauvegarde des objets sérialisés 
		 */
		public void fermerVue (Vue vue) {
			//la vue est détruite et n'est plus la vue active courante	
			
			if (vue instanceof VueMenuBiblio ) {
			// Quitte l'aplication. Sauvegarde les objets du modèle
				vue.dispose();
				this.removeVue();this.sauve();
				System.exit(0);
				}
			else {
			// le Menu est rendu de nouveau visible
				vue.dispose();
				this.removeVue();
				this.getVueMenu().setVisible(true); 
			}
		}
		
		/*
		// ************************************************************************************************************
		// Opérations liées à la sérialisation des objets de l'application
		// ************************************************************************************************************
		/**
		 *  restauration des objets de l'application
		 */
		public Controleur restaure() {
			try {
				FileInputStream fichier = new FileInputStream("Fsauv.ser");
				ObjectInputStream in = new ObjectInputStream(fichier);
				return((Controleur) in.readObject());
			} catch (Exception e) {
				Message dialog = new Message("Pbs de Restauration ou fichier non encore créé");
				return this;
			} 
		}
		/**
		 *  sauvegarde des objets de l'application
		 */
		private void sauve() {
			try {
				FileOutputStream f = new FileOutputStream("Fsauv.ser");
				ObjectOutputStream out = new ObjectOutputStream(f);
				out.writeObject(this);
			} catch (Exception e) {
				Message dialog = new Message("Pb de Sauvegarde dans le fichier");
			}
		}
		// ************************************************************************************************************
		// Opérations liées à l'application en réponse à une action de l'utilisateur dans une vue
		// ************************************************************************************************************

		/**
		 * Accès à un ouvrage par son numéro ISBN
		 * Invoqué dans VueConsultOuvrage et VueSaisieExemplaire
		 * @param isbn 	le code ISBN de l'ouvrage cherché
		 * @return l'ouvrage qui a l'ISBN indiqué ou null
		 * affiche un message d'erreur si l'ouvrage n'est pas trouvé
		 */
		public Ouvrage rechOuvrage(String isbn) {
			Ouvrage ouv = this.getOuvrage(isbn);
			if (ouv == null) {
				Message dialog = new Message("Ouvrage inconnu");
			}
			else {
				// la vue courante est VueSaisieExemplaire
				if (this.getVue() instanceof VueSaisieExemplaire){
				// la vue est inscrite comme observatrice de l'ouvrage	
					ouv.addObserver(this.getVue());
				// le contrôleur modifie l'état de la vue
					this.getVue().setEtat(Vue.inter1);	
				// le controleur demande à la vue d'afficher les infos de l'ouvrage
					((VueSaisieExemplaire)this.getVue()).alimente(ouv);
					}
				// la vue courante est VueConsultOuvrage
				if (this.getVue() instanceof VueConsultOuvrage){
					// le contrôleur modifie l'état de la vue
					this.getVue().setEtat(Vue.finale);
					((VueConsultOuvrage)this.getVue()).alimente(ouv);
					}
			}
			return ouv;
		} // Fin rechOuvrage
		
		public Lecteur rechLecteur(int numLect) {
			Lecteur lect = this.getLecteur(numLect);
			if (lect == null) {
				Message dialog = new Message("Lecteur inconnu");
			}
			else {
				if (this.getVue() instanceof VueConsultLecteur){
					// le contrôleur modifie l'état de la vue
					this.getVue().setEtat(Vue.finale);
					((VueConsultLecteur)this.getVue()).alimente(lect);
					}
			}
			return lect;
		}
		
		/*public Auteur rechAuteur(String auteur) {
			Auteur aut = this.getAuteur(auteur);
			if (aut == null) {
			
			}
		}*/
		
		public Periodique rechercherInfosPeriodique(String issn) {
			Periodique per = this.getPeriodique(issn);
			if (per == null) {
				Message dialog = new Message("Periodique inconnu");
			}
			else {
				// la vue courante est VueConsultPeriodique
				if (this.getVue() instanceof VueConsultPeriodique) {
				// la vue est inscrite comme observatrice du périodique	
					per.addObserver(this.getVue());
				// le contrôleur modifie l'état de la vue
					this.getVue().setEtat(Vue.finale);
				// le contrôleur demande à la vue d'afficher les infos du périodique
					((VueConsultPeriodique)this.getVue()).alimente(per);					
				}
			}
			return per;
		}
		
		/**
		 * Création d'un exemplaire d'ouvrage 
		 * Invoqué dans VueSaisieExemplaire
		 * @param ouv l'ouvrage  dateRecep la date de réception de l'exemplaire	
		 * affiche un message de confirmation après l'enregistrement ou un message d'erreur 
		 */
		public void nouvExemplaire(Ouvrage ouv, String dateReception, String statut) {
			// vérification de la présence de la date et de son format
			if (dateReception.length() == 0 ){
					Message dialog = new Message("La date de réception est obligatoire");
					}
			else {
				GregorianCalendar date = ESDate.lireDate (dateReception);
				if (date == null) {
					Message dialog = new Message("Le format de la date est incorrect");
					}
				else {
					int statutEx;
					if (statut == "empruntable") {
						statutEx = Exemplaire.EMPRUNTABLE ; }
					else {
						statutEx = Exemplaire.EN_CONSULTATION ; }
			// demande d'ajout de l'exemplaire
					Exemplaire exemplaire = ouv.ajouterExemplaire(date, statutEx);
			// l'opération s'est bien passée
					if (exemplaire != null) {
			// le contrôleur modifie l'état de la vue
						this.getVue().setEtat(Vue.finale);
			// affichage d'un message de confirmation
						Message dialog = new Message("Exemplaire enregistré");
					}
					else {
						Message dialog = new Message("Date de Reception incorrecte / à la date d'Edition.");
					}
				}
			}
		} // Fin nouvExemplaire
		
		/**
		 * Création d'un  d'ouvrage 
		 * Invoqué dans VueSaisieOuvrage
		 * @param  dateEdition la date d'édition de l'ouvrage
		 * affiche un message de confirmation après l'enregistrement ou un message d'erreur 
		 */
		public void nouvOuvrage(String isbn, String titre, String auteur, String editeur, String dateEdition) {
			// vérification de la présence des infos obligatoires et du format de la date
			if ((isbn.length() == 0) || (titre.length() == 0) || (auteur.length() == 0)
					|| (editeur.length() == 0 )|| (dateEdition.length() == 0 )){
					Message dialog = new Message("Tous les champs sont obligatoires");
					}
			else {
				GregorianCalendar date = ESDate.lireDate (dateEdition);
				if (date == null) {
					Message dialog = new Message("Le format de la date est incorrect");
					}
				else if (this.getOuvrage(isbn )== null) {
					
				// Instanciation de l'ouvrage
					Ouvrage ouvrage = new Ouvrage(isbn, titre, auteur, editeur, date);
					Auteur auteur1= new Auteur(auteur.split(" ")[0],auteur.split(" ")[1]);
					auteur1.lierOuvrage(ouvrage);
				// Ajout de l'ouvrage dans l'ensemble des ouvrages de la bibliothèque
					this.setOuvrage(ouvrage, isbn);
					this.setAuteur(auteur1,auteur);
					
					Message dialog = new Message("Ouvrage enregistré");
					this.fermerVue (this.getVue());
					} 
					else {
						Message dialog = new Message("Ouvrage déjà présent");
					}
				}
			}// Fin nouvOuvrage
		
		public void nouvQuotas(String nbMaxEmprunt, String dureeEmprunt, String montantPenalite) {
			// vérification de la présence des infos obligatoires
			if (nbMaxEmprunt.length() == 0 || dureeEmprunt.length() == 0 || montantPenalite.length() == 0) {
				Message dialog = new Message("Tous les champs sont obligatoires");
			}
			
			else {
				
				Quotas quotas = new Quotas(Integer.parseInt(nbMaxEmprunt), Integer.parseInt(dureeEmprunt), Float.parseFloat(montantPenalite));
				this.setQuotas(quotas);
			
				this.getVue().setEtat(Vue.finale);
				Message dialog = new Message("Quotas paramétrés");
				this.fermerVue (this.getVue());
			}			
		}
		
		public void nouvLecteur(String nom, String prenom, String adresse, String tel) {
			if (nom.length() == 0 || prenom.length() == 0 || adresse.length() == 0 || tel.length() == 0) {
				Message dialog = new Message("Tous les champs sont obligatoires");}
			else if (tel.length() != 10) {
				Message dialog = new Message("Un numéro de téléphone doit être composé de 10 chiffres"); }
			else {
				Lecteur lecteur = new Lecteur(derNumLect, nom, prenom, adresse, tel);
				// tester exception 00.00.00.00.0a
				this.setLecteur(lecteur, derNumLect);
				derNumLect++;
				
				this.getVue().setEtat(Vue.finale);
				Message dialog = new Message("Lecteur enregistré");
				this.fermerVue (this.getVue());
		}	
		}
		
		public void nouvPeriodique (String issn, String nomP) {
			if (issn.length() == 0 || nomP.length() == 0) {
				Message dialog = new Message ("Tous les champs sont obligatoires");}
			else {
				Periodique periodique = new Periodique(issn, nomP);
				
				this.setPeriodique(periodique, issn);
				
				this.getVue().setEtat(Vue.finale);
				Message dialog = new Message("Périodique enregistré");
				this.fermerVue (this.getVue()); 
			}
		}

		public Periodique rechPerio(String issn){
			Periodique pe ;

			pe=this.getPeriodique(issn);
			return pe;
			
					
		}
		
		public Parution enregistrerParution(String identifiant, Periodique pe){
			Parution pa;
			if (identifiant.length()==0){
				Message dialog = new Message ("Tous les champs sont obligatoires");
				pa=null;
			}
			else
			{
				pa= new Parution(identifiant,pe);
				((VueSaisieParution)getVue()).alimenteAuteurs(this.getAuteurs());
				getVue().setEtat(getVue().inter2);
			}
				return pa;
			}
		
		public void enregistrerArticle(String titre, String auteur, Parution pa){
			Auteur au;
			Article ar;
			String nomAuteur;
			String prenomAuteur;
			if (titre.length()==0 || auteur.length()==0){
				Message dialog = new Message ("Tous les champs sont obligatoires");}
			else{
				au=this.getAuteur(auteur);
				if (au==null){
					nomAuteur = auteur.split(" ")[0];
					prenomAuteur = auteur.split(" ")[1];
					au = new Auteur(nomAuteur,prenomAuteur);
					this.setAuteur(au, nomAuteur+" "+prenomAuteur);
				}		
					pa.addObserver(this.getVue());
					ar = new Article(titre,au,pa);
					Message dialogue = new Message("Article enregistré.");
					getVue().setEtat(getVue().inter3);
			}
			}
			
}

		
	

